/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *                                                                           *
 *                          Copyright (c) 2010                               *
 *                  Alen Stojanov [ ioi.alen@gmail.com ]                     *
 *                Jacobs University Bremen, Bremen, Germany                  *
 *                                                                           *
 *                          All Rights Reserved.                             *
 *                                                                           *
 *    Permission to use, copy, modify and distribute this software and       *
 *    its documentation is hereby granted, provided that both the            *
 *    copyright notice and this permission notice appear in all copies of    *
 *    the software, derivative works or modified versions, and any           *
 *    portions thereof, and that both notices appear in supporting           *
 *    documentation.                                                         *
 *                                                                           *
 *    This code is distributed in the hope that it will be useful, but       *
 *    WITHOUT ANY WARRANTY; without even the implied warranty of             *
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS       *
 *    DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER          *
 *    RESULTING FROM THE USE OF THIS SOFTWARE.                               *
 *                                                                           *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <QFileDialog>
#include <QFileInfo>
#include <QListWidgetItem>

#include "tsview.h"
#include "tsdata.h"
#include "tsdataimport.h"
#include "gllinegraph.h"

/* ======================================================================== */
/* 					  	Handle Threaded Data Read							*/
/* ======================================================================== */

TSDataRead::TSDataRead (QStringList fileNames, TSData::TSDataType dataType, TSView* tsview) :
TSThread(tsview->ui.progressBar, tsview->ui.lblProgress)
{
	this->fileNames	 = fileNames;
	this->dataType	 = dataType;
	this->tsview	 = tsview;

	this->setProgressBar(this->tsview->ui.progressBar);
}

void TSDataRead::run ()
{
	if ( fileNames.size() > 0 ) {
		QVector<TSData*> newSeries;
		complete_qint64 = 0;
		for (int i = 0; i < fileNames.size(); ++i) {
			QString fileName = fileNames.at(i);
			if ( fileName != "" ) {
				QFile file(fileName);
				complete_qint64 += file.size();
			}
		}
		for (int i = 0; i < fileNames.size(); ++i) {
			QString fileName = fileNames.at(i);
			if ( fileName != "" ) {
				QFile file(fileName);
				if ( file.exists() ) {
					QFileInfo fileInfo(file);
					/* use the basename as name of the TSData */
					QString name = fileInfo.baseName();
					updateText("Reading: <b>" + name + "</b>");
					if ( tsview->mapNames2TSData.contains(name) ) {
						name = tsview->getFreshName(name);
					}
					TSData *data = new TSData(name, dataType, tsview->getColorByIndex());
					data->readData(file, this);
					tsview->mapNames2TSData.insert(name, data);
					newSeries.push_back(data);
					partial_qint64 += file.size();
				}
			}
		}
		updateText("");
		update(0.0);
		emit tsview->seriesAdded(newSeries);
	}
}

TSDataRead::~TSDataRead ()
{
	// Do nothing
}

/* ======================================================================== */
/* 			Create the TSView and connect all slots & signals				*/
/* ======================================================================== */

TSView::TSView(QWidget *parent, QString fileName): QWidget(parent)
{
	controlPointsSize = 0;

	/* create the OpenGL Graphs & Previews */
	glLineGraph	= new GLLineGraph(this);
	glPreview 	= new GLPreview(this);
	glLSPGraph	= new GLLSPGraph(this);

	ui.setupUi(this);
	//
	// Connect "new series added" signals and slots
	//
	/* TSView (addSeries) ---> TSView */
	connect(this, SIGNAL(seriesAdded(QVector<TSData*>)),
			this, SLOT(slot_on_seriesAdded(QVector<TSData*>)));
	/* TSView (addSeries) ---> GLPreview */
	connect(this, SIGNAL(seriesAddedPreview(QVector<TSData*>)),
			glPreview, SLOT(on_addSeries(QVector<TSData*>)));
	/* TSView (addSeries) ---> GLLSPGraph */
	connect(this, SIGNAL(seriesAddedLSPGraph(QVector<TSData*>)),
			glLSPGraph, SLOT(on_addSeries(QVector<TSData*>)));
	/* TSView (addSeries) ---> GLLineGraph */
	connect(this, SIGNAL(seriesAddedLineGraph(QVector<TSData*>)),
			glLineGraph, SLOT(on_addSeries(QVector<TSData*>)));
	//
	// Connect line graph range change
	//
	/* GLPreview (rangeChanged) ---> TSView */
	connect(glPreview, SIGNAL(rangeChanged(double, double)),
			this, SLOT(slot_on_glLineGraph_updateRange(double, double)));
	/* TSView (glLineGraph_updateRange) ---> GLLineGraph */
	connect(this, SIGNAL(glLineGraph_updateRange(double, double)),
			glLineGraph, SLOT(on_updateRange(double, double)));
	//
	// Connect line graph zoom
	//
	connect(this, SIGNAL(glPreview_percentChange(double)),
			glPreview, SLOT(on_rangePercent_change(double)));
	//
	// Connect chance of control points
	//
	connect(this, SIGNAL(CTRLPointsValueChanged(int)),
			glLSPGraph, SLOT(on_updateControlPointsSize(int)));
	connect(this, SIGNAL(glLineGraph_highlight(QVector<TSData*>)),
			glLineGraph, SLOT(on_dataHighlight (QVector<TSData*>)) );

	//
	// LSP graph data selection
	//
	connect(glLSPGraph, SIGNAL(selectTSData(QVector<TSData*>)),
			this, SLOT(slot_on_selectTSData(QVector<TSData*>)) );

	/* add the widgets to the wrappers */
	ui.glMantraWrap->addWidget(glLineGraph);
	ui.glPreviewWrap->addWidget(glPreview);
	ui.glSinglePreviewWrap->addWidget(glLSPGraph);

	/* set the color index */
	colorIndex = 0;

	if ( fileName == "" ) {
		this->setWindowTitle("New Render Document");
	} else {
		// TODO: Implement open rendered document
	}
}

/* ======================================================================== */
/* 					  		 Handle Data Actions							*/
/* ======================================================================== */

void TSView::on_cmdImport_clicked()
{
	/* get the data selection view */
	TSDataImport* tsdataimport = new TSDataImport();
	tsdataimport->exec();

	/* get the file list and the data type */
	QStringList fileList 		= tsdataimport->getFiles();
	TSData::TSDataType dataType	= tsdataimport->getDataType();

	/* create the thread */
	TSDataRead *tsdataread = new TSDataRead(fileList, dataType, this);

	/* start reading the new data */
	tsdataread->start();

	/* get rid of the window */
	delete tsdataimport;
}

void TSView::slot_on_seriesAdded (QVector<TSData*> newSeries)
{
	for (int i = 0; i < newSeries.size(); i++) {
		TSData* data = newSeries.at(i);
		QPixmap iconTSPixmap(32,32); iconTSPixmap.fill(data->getColor());
		QListWidgetItem *newItem = new QListWidgetItem(QIcon(iconTSPixmap), data->getName());
		ui.listData->addItem(newItem);
	}

	if ( newSeries.size() > 0 ) {
		ui.sliderZoom->setValue(20);
		ui.sliderZoom->setEnabled(true);
		ui.sliderCTRLPoints->setRange(1, mapNames2TSData.size());
		ui.spinCTRLPoints->setRange(1, mapNames2TSData.size());
	}

	/* add the series in the preview graph */
	emit seriesAddedPreview(newSeries);
	emit glPreview_percentChange(20.0);

	/* add the series in the Line Graph */
	emit seriesAddedLineGraph(newSeries);
	/* add the series in the LSP graph */
	emit seriesAddedLSPGraph(newSeries);

	/* calculate the control points */
	controlPointsSize = floor(sqrt(mapNames2TSData.size()));
	ui.sliderCTRLPoints->setValue(controlPointsSize);
	emit CTRLPointsValueChanged(controlPointsSize);
}

/* ======================================================================== */
/* 				Handle Line Graph & Preview Graph Manipulation				*/
/* ======================================================================== */

void TSView::on_sliderZoom_valueChanged(int percent)
{
	if ( percent > 100 ) {
		percent = 100;
	}
	ui.sliderZoom->setValue(percent);
	lineGraphZoomFactor = percent;
	ui.doubleSpinLineGraphZoomPercent->setValue(percent);
	emit glPreview_percentChange((double)percent);
}

void TSView::slot_on_glLineGraph_updateRange(double rangeMinX, double rangeMaxX)
{
	emit glLineGraph_updateRange(rangeMinX, rangeMaxX);
}

void TSView::on_cmdZoomInLineGraph_clicked()
{
	if ( lineGraphZoomFactor >= 100 ) {
		return;
	}
	if ( lineGraphZoomFactor > 1 ) {
		on_sliderZoom_valueChanged((int)lineGraphZoomFactor - 1);
	} else {
		lineGraphZoomFactor /= 2;
		ui.doubleSpinLineGraphZoomPercent->setValue(lineGraphZoomFactor);
		emit glPreview_percentChange(lineGraphZoomFactor);
	}
}

void TSView::on_cmdZoomOutLineGraph_clicked()
{
	if ( lineGraphZoomFactor >= 1 ) {
		on_sliderZoom_valueChanged((int)lineGraphZoomFactor + 1);
	} else {
		lineGraphZoomFactor *= 2;
		if ( lineGraphZoomFactor >= 1 ) {
			on_sliderZoom_valueChanged((int)lineGraphZoomFactor);
		} else {
			ui.doubleSpinLineGraphZoomPercent->setValue(lineGraphZoomFactor);
			emit glPreview_percentChange(lineGraphZoomFactor);
		}
	}
}

void TSView::on_listData_itemSelectionChanged()
{
	QVector<TSData*> selectedTSData;

	QList<QListWidgetItem *> selectedItems = ui.listData->selectedItems();
	QList<QListWidgetItem *>::iterator it;

	for ( it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
		TSData* data = mapNames2TSData[(*it)->text()];
		selectedTSData.push_back(data);
	}

	emit glLineGraph_highlight(selectedTSData);
}

/* ======================================================================== */
/* 						Handle LSP Graph Manipulation						*/
/* ======================================================================== */

void TSView::on_sliderCTRLPoints_valueChanged(int value)
{
	ui.spinCTRLPoints->setValue(value);
	emit CTRLPointsValueChanged(value);
}

void TSView::on_spinCTRLPoints_valueChanged(int value)
{
	ui.sliderCTRLPoints->setValue(value);
	emit CTRLPointsValueChanged(value);
}

void TSView::slot_on_selectTSData(QVector<TSData*> series)
{
	for ( int i = 0; i < ui.listData->count(); ++i ) {
		QListWidgetItem *item = ui.listData->item(i);
		TSData *data = mapNames2TSData[item->text()];
		if ( series.contains(data) ) {
			item->setSelected(true);
		} else {
			item->setSelected(false);
		}
	}
	on_listData_itemSelectionChanged();
}

/* ======================================================================== */
/* 				Handle OpenGL windows & settings bar toggle					*/
/* ======================================================================== */

void TSView::toggleLineGraph()
{
	if ( this->glLineGraph->isHidden() ) {
		this->glLineGraph->show();
	} else {
		this->glLineGraph->hide();
	}
	this->update();
}

void TSView::toggleLSPGraph()
{
	if ( this->glLSPGraph->isHidden() ) {
		this->glLSPGraph->show();
	} else {
		this->glLSPGraph->hide();
	}
	this->update();
}

void TSView::togglePreviewGraph ()
{
	if ( this->glPreview->isHidden() ) {
		this->glPreview->show();
	} else {
		this->glPreview->hide();
	}
	this->update();
}

void TSView::toggleSettings()
{
	if ( this->ui.widgetSettings->isHidden() ) {
		this->ui.widgetSettings->show();
	} else {
		this->ui.widgetSettings->hide();
	}
	this->update();
}

/* ======================================================================== */
/* 					Handle Data naming and coloring							*/
/* ======================================================================== */

QString TSView::getFreshName (QString name)
{
	if ( !mapNameIndex.contains(name) ) {
		mapNameIndex.insert(name, 0);
	}
	int index = ++mapNameIndex[name];
	return name.append( "_" + QString::number(index) );
}

QColor TSView::getColorByIndex ()
{
	int index = colorIndex++;

	/* get the color component */
	unsigned char value = index / 8;
	if ( value > (char)256 ) {
		value = value % 256;
	}
	/* we go backwards */
	value = 255 - value;

	/* reverse the bits MSB to LSB */
	int tmp = 0;
	for (int i = 0; i < 8; i++) {
		tmp = tmp * 2 + value % 2;
		value = value / 2;
	}
	value = tmp;

	/* generate colors for the 8 different quadrants */
	QColor quadrant[8];
	quadrant[0] = QColor::fromRgb(			 0,   		  0,       value);
	quadrant[1] = QColor::fromRgb(			 0, 		255, 255 - value);
	quadrant[2] = QColor::fromRgb(		 value, 		  0, 		 255);
	quadrant[3] = QColor::fromRgb(		 value,		    255, 		   0);
	quadrant[4] = QColor::fromRgb(		   255,		  value,		 255);
	quadrant[5] = QColor::fromRgb(		   255,	255 - value,		   0);
	quadrant[6] = QColor::fromRgb(255 - value, 			255, 		 255);
	quadrant[7] = QColor::fromRgb(255 - value, 			  0, 		   0);

	/* with this assignment of colors, gray can never be reached */
	/* we use this color to replace white */
	if ( quadrant[index % 8] == QColor::fromRgb(255, 255, 255) ) {
		return QColor::fromRgb(128, 128, 128);
	} else {
		return quadrant[index % 8];
	}
}

/* ======================================================================== */
/* 									Other									*/
/* ======================================================================== */

TSView::~TSView()
{
	/* delete all the data used in the render */
	QList<TSData*> list = mapNames2TSData.values();
	while ( list.size() > 0 ) {
		delete list.first();
		list.removeFirst();
	}
}
